﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace engine
{
    public static class TemplateFuncNames
    {
        public const string PropOfObject = "prop";
        public const string ItemOfArray = "item";
    }

    public class TemplateFunc : TemplateFuncBase
    {
        public TemplateFunc(FuncType funcType): base(funcType)
        {

        }

        [RenderTemplateFunc(AppliedType = UnitType.Row, Name = "row")]
        public List<Unit> Row(RowNode row, For f)
        {
            List<Unit> rows = new List<Unit>();
            f.Run(row, (list, item, index) =>
            {
                RowUnit temp = new RowUnit();
                foreach (var child in row.Children)
                {
                    var cells = child.RenderValue(null);
                    foreach (var cell in cells)
                    {
                        temp.Cells.Add(cell as CellUnit);
                    }
                }
                rows.Add(temp);
            });

            return rows;
        }

        [RenderTemplateFunc(AppliedType = UnitType.Any, Name = TemplateFuncNames.PropOfObject)]
        public object GetProp(NodeBase node, Dictionary<string, object> obj, string objName, string propName)
        {
            if (obj == null)
            {
                throw new Exception(string.Format("对象未定义"));
            }
            if (!obj.ContainsKey(propName))
            {
                throw new Exception(string.Format("属性{0}不存在", propName));
            }

            DataItem dataItem = node.FuncNode.GetDataWithParent(objName);
            if (!(dataItem.Config is ObjectVarItem))
            {
                throw new Exception(string.Format("属性{0}不是对象", objName));
            }

            TypeItem typeItem = (dataItem.Config as ObjectVarItem).CustomType;
            if(typeItem == null)
            {
                return obj[propName];
            }
            else
            {
                if (typeItem.Props.ContainsKey(propName))
                {
                    var config = typeItem.Props[propName];
                    return config.FormatDataOrDefault(obj[propName]);
                }
                else
                {
                    throw new Exception(string.Format("属性{0}不存在", propName));
                }
            }
        }

        [RenderTemplateFunc(AppliedType = UnitType.Any, Name = TemplateFuncNames.ItemOfArray)]
        public object GetItem(NodeBase node, object obj, string arrName, object index)
        {
            if(obj is List<object>)
            {
                var arr = obj as List<object>;
                if (arr == null)
                {
                    throw new Exception(string.Format("数组未定义"));
                }
                else if ((int)index >= arr.Count)
                {
                    throw new Exception(string.Format("超出数组索引范围"));
                }
                else
                {
                    return arr[(int)index];
                }
            }
            else if(obj is Dictionary<string, object>)
            {
                var dic = obj as Dictionary<string, object>;
                if (dic.ContainsKey(index.ToString()))
                {
                    return dic[index.ToString()];
                }
                else
                {
                    throw new Exception(string.Format("属性{0}不存在", index.ToString()));
                }
            }

            return null;
        }

        [RenderTemplateFunc(AppliedType = UnitType.String, Name = "tostr")]
        public string ToStr(NodeBase node, object obj)
        {
            if(obj is Unit)
            {
                return (obj as Unit).ToString();
            }
            else if(obj is List<Unit>)
            {
                var list = obj as List<Unit>;
                var body = new BodyUnit();
                body.Children = list;
                return body.ToString();
            }
            else
            {
                return obj.ToString();
            }
        }
        
        [RenderTemplateFunc(AppliedType = UnitType.Picture, Name = "topic")]
        public PictureUnit ToPic(NodeBase node, string picName, int width, int height)
        {
            Image image = new Bitmap(picName);
            PictureUnit pictureUnit = new PictureUnit();
            pictureUnit.Image = image;
            if(width > 0)
            {
                pictureUnit.Width = width;
            }
            if(height > 0)
            {
                pictureUnit.Height = height;
            }
            return pictureUnit;
        }

        [RenderTemplateFunc(AppliedType = UnitType.Array, Name = "subarr")]
        public List<object> SubArr(NodeBase node, List<object> arr, int beginIndex, int length)
        {
            if(arr is List<object>)
            {
                var list = arr as List<object>;
                if(list.Count >= beginIndex + length)
                {
                    return list.Skip(beginIndex).Take(length).ToList();
                }
                else
                {
                    throw new Exception("subarr函数要截取的范围超出对象长度");
                }
            }
            else
            {
                throw new Exception("subarr函数要截取的对象不是数组");
            }
        }

        [RenderTemplateFunc(AppliedType = UnitType.Array, Name = "regexmatches")]
        public List<object> RegexMatches(NodeBase node, string str, string pattern, int groupIndex)
        {
            MatchCollection match = Regex.Matches(str, pattern);
            var list = new List<object>();
            var temp = match.GetEnumerator();
            while (temp.MoveNext())
            {
                list.Add((temp.Current as Match).Groups[groupIndex].Value);
            }
            return list;
        }

        //*****************************************************************************************************************
        [MatchTemplateFunc(AppliedType = UnitType.Row, Name = "row")]
        public void MatchRow(RowNode row, List<Unit> units, For f)
        {
            f.RunForMatch(row, units, unit =>
            {
                foreach (var cell in row.Children)
                {
                    cell.MatchData(unit.Children, row.FuncNode,  null);
                }
            });
        }

        [MatchTemplateFunc(AppliedType = UnitType.Any, Name = TemplateFuncNames.PropOfObject)]
        public void MatchProp(NodeBase node, List<Unit> units, Dictionary<string, object> obj, string objName, string propName)
        {
            // 匹配数据
            object value = node.FuncNode.DefaultMatchData(node, units);
            
            obj[propName] = value;
        }

        [MatchTemplateFunc(AppliedType = UnitType.Any, Name = TemplateFuncNames.ItemOfArray)]
        public void MatchItem(NodeBase node, List<Unit> units, List<object> arr, string arrName, int index)
        {
            // 匹配数据
            object value = node.FuncNode.DefaultMatchData(node, units);

            while (arr.Count <= index)
            {
                arr.Add(null);
            }
            arr[index] = value;
        }
    }
    public interface ITemplateFunc
    {
        object Run(string funcName, object[] paras);
        UnitType? GetFuncType(string funcName);
        bool HasFunc(string funcName);
    }

    public enum FuncType
    {
        Match,
        Render
    }

    public abstract class TemplateFuncBase : ITemplateFunc
    {
        public FuncType FuncType { get; set; }
        Dictionary<string, MethodInfo> funcMap { get; set; }
        Dictionary<string, TemplateFuncAttributeBase> funcAttributeMap { get; set; }

        public TemplateFuncBase(FuncType funcType)
        {
            funcMap = new Dictionary<string, MethodInfo>();
            funcAttributeMap = new Dictionary<string, TemplateFuncAttributeBase>();

            Type type = this.GetType();
            MethodInfo[] methods = type.GetMethods();
            if(funcType == FuncType.Match)
            {
                foreach (MethodInfo method in methods)
                {
                    MatchTemplateFuncAttribute templateFuncAttribute = method.GetCustomAttribute<MatchTemplateFuncAttribute>();
                    if (templateFuncAttribute == null)
                    {
                    }
                    else
                    {
                        if (templateFuncAttribute.Name != null && templateFuncAttribute.Name.Trim().Length > 0)
                        {
                            funcMap.Add(templateFuncAttribute.Name, method);
                            funcAttributeMap.Add(templateFuncAttribute.Name, templateFuncAttribute);
                        }
                        else
                        {
                            funcMap.Add(method.Name, method);
                        }
                    }
                }
            }
            else if(funcType == FuncType.Render)
            {
                foreach (MethodInfo method in methods)
                {
                    RenderTemplateFuncAttribute templateFuncAttribute = method.GetCustomAttribute<RenderTemplateFuncAttribute>();
                    if (templateFuncAttribute == null)
                    {
                    }
                    else
                    {
                        if (templateFuncAttribute.Name != null && templateFuncAttribute.Name.Trim().Length > 0)
                        {
                            funcMap.Add(templateFuncAttribute.Name, method);
                            funcAttributeMap.Add(templateFuncAttribute.Name, templateFuncAttribute);
                        }
                        else
                        {
                            funcMap.Add(method.Name, method);
                        }
                    }
                }
            }
        }

        public object Run(string funcName, object[] paras)
        {
            MethodInfo method = GetMethodInfo(funcName);
            ParameterInfo[] pis = method.GetParameters();

            List<object> handledParas = new List<object>();
            //handledParas.Add(paras[0]);
            for (int i = 0; i < pis.Length && i < paras.Length; i++)
            {
                object temp = null;
                if (pis[i].ParameterType == typeof(object))
                {
                    temp = paras[i];
                }
                else if (paras[i] is IConvertible)
                {
                    temp = Convert.ChangeType(paras[i], pis[i].ParameterType);
                }
                else
                {
                    temp = paras[i];
                }
                handledParas.Add(temp);
            }

            object result = method.Invoke(this, handledParas.ToArray());
            return result;
        }

        public UnitType? GetFuncType(string funcName)
        {
            if (funcAttributeMap.ContainsKey(funcName))
            {
                TemplateFuncAttributeBase templateFuncBase = funcAttributeMap[funcName];
                return templateFuncBase.AppliedType;
            }
            else
            {
                return null;
            }
        }

        public bool HasFunc(string funcName)
        {
            MethodInfo method = GetMethodInfo(funcName, false);
            return method != null;

        }

        public MethodInfo GetMethodInfo(string funcName, bool shouldThrowException = true)
        {
            if (funcMap.ContainsKey(funcName))
            {
                return funcMap[funcName];
            }
            else
            {
                if (shouldThrowException)
                {
                    throw new Exception(string.Format("模板函数{0}不存在", funcName));
                }
                else
                {
                    return null;
                }
            }
        }
    }

    [AttributeUsage(AttributeTargets.Method)]
    public class RenderTemplateFuncAttribute : TemplateFuncAttributeBase
    {
    }

    [AttributeUsage(AttributeTargets.Method)]
    public class MatchTemplateFuncAttribute : TemplateFuncAttributeBase
    {
    }

    public abstract class TemplateFuncAttributeBase: Attribute
    {
        public UnitType AppliedType { get; set; }
        public string Name { get; set; }
    }
}
